/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kiwi.stanbol.services.enhancer;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;

import kiwi.core.api.config.ConfigurationService;
import kiwi.core.api.sesame.KiWiSesameService;
import kiwi.core.exception.KiWiException;
import kiwi.core.exception.ParsingException;
import kiwi.core.model.content.MediaContentItem;
import kiwi.core.model.rdf.KiWiMediaContentLiteral;
import kiwi.core.model.rdf.KiWiResource;
import kiwi.core.model.rdf.KiWiTriple;
import kiwi.core.services.io.RdfXmlParser;

import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryException;
import org.slf4j.Logger;

/**
 * @author Rupert Westenthaler
 *
 */
public class StanbolEnhancer implements ContentEnhancer {

    /**
     * The key used with the {@link ConfigurationService} to configure the 
     * location of the Apache Stanbol Enhancer service used to enhancer content
     * items parsed to this component.
     */
    public static final String STANBOL_ENHANCER_SERVICE_URL_KEY = "enhancer.stanbol.enhancer.url";
    /**
     * The RDF/XML content type ("application/rdf+xml")
     */
    public static final String RDF_XML = "application/rdf+xml";
	
    @Inject
    private Logger log;

    @Inject
    private RdfXmlParser parser;

    @Inject
    private KiWiSesameService sesameService;

    @Inject
    private ConfigurationService configurationService;

//    @Inject
//    private ContentItemService contentItemService;


//    @Inject
//    private TaggingService taggingService;

    /**
     * Stores the location of the Apache Stanbol Enhancer service. Set by the
     * {@link #activate()} method.
     */
    private URL serviceLocation;
	
    @PostConstruct
    public final void activate() throws MalformedURLException, IllegalArgumentException {
        log.info("registering Apache Stanbol Enhancer ...");
        String serviceLocation = configurationService.getStringConfiguration(STANBOL_ENHANCER_SERVICE_URL_KEY);
        log.info("Activate with Stanbol Server Location \""+serviceLocation+"\"");
        if(serviceLocation == null || serviceLocation.isEmpty()){
            throw new IllegalStateException(String.format("Missing configuration for %s (this parameter MUST BE set to the Apache Stanbol Enhancer Service (e.g. http://localhost:8080/engines)",
                STANBOL_ENHANCER_SERVICE_URL_KEY));
        } else {
            this.serviceLocation = new URL(serviceLocation);
        }
    }
	
    @PreDestroy
    public final void deactivate(){
        this.serviceLocation = null;
    }

    public final void enhance(MediaContentItem contentItem) throws KiWiException {
        if(contentItem == null){
            throw new NullPointerException("The parsed Content Item MUST NOT be NULL!");
        }
        KiWiResource resource = contentItem.getDelegate();
        if(resource == null){
            throw new IllegalStateException(String.format("Delegate for MediaContentItem %s is NULL!",contentItem));
        }
        if(resource.isLiteral()){
            throw new IllegalStateException(String.format("Delegate of MediaContentItem %s is of type Literal (value=%s)",
                contentItem,resource));
        } //else we can use it
        KiWiMediaContentLiteral content = contentItem.getMediaContent();
        if(content == null){
            throw new IllegalStateException(String.format("Content (KiWiMediaContentLiteral) for MediaContentItem %s is NULL!",
                contentItem));
        }
        Repository enhancements;
        try {
            enhancements = getEnhancements(resource, content);
        }  catch (IOException e) {
            log.error(e.getMessage(),e);
            throw new KiWiException("Unable to connect to remote Service "+serviceLocation);
        } catch (ParsingException e) {
            log.error(e.getMessage(),e);
            throw new KiWiException("Unable to parse response from Service "+serviceLocation);
        }
        try {
            Set<KiWiTriple> triples = sesameService.importFromSesame(enhancements.getConnection());
            log.info("Enhancement Results:");
            for(KiWiTriple triple : triples){
                log.info(triple.toN3());
            }
            //currently we need nothing to do with this triples!
        } catch (RepositoryException e) {
            log.error(e.getMessage(),e);
            throw new KiWiException(String.format("Unable to import computed Enhancement by using KiwiSesameService %s and Repository %s",
                sesameService,enhancements));
        }
    }
    
    protected final Repository getEnhancements(KiWiResource resource, KiWiMediaContentLiteral content) throws IOException, IllegalStateException, ParsingException {
        log.info("Calculate enhancement for Resource "+resource+" using Stanbol Enhancer Service at "+serviceLocation);
        URL requestURL = new URL(serviceLocation.toString()+'?'+resource.toString());
        byte[] data = content.getData();
        HttpURLConnection connection = (HttpURLConnection) requestURL.openConnection();
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        //set the content length to the size of the data
        connection.addRequestProperty("Content-Length", Integer.toString(data.length));
        //set the accept header to the content type supported by the parser
        connection.addRequestProperty("Accept", RDF_XML);
        //set the content type the the mime type of the parsed content item
        if(content.getMimeType() == null ||content.getMimeType().isEmpty()){
            throw new IllegalStateException(String.format("The parsed content %s does not define an mime-type \"%s\"!",
                content, content.getMimeType()));
        }
        connection.addRequestProperty("Content-type", content.getMimeType());
        DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
        dataOutputStream.write(data);
        dataOutputStream.close();
        return parser.parseTriples(connection.getInputStream());
    }
}
